﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MathematicallySafe.HeadCounter.Data;
using MathematicallySafe.HeadCounter.Data.Common;
using MathematicallySafe.HeadCounter.Data.Domain;
using MathematicallySafe.HeadCounter.Data.Interfaces;
using MathematicallySafe.HeadCounter.Data.Models;
using StructureMap;

namespace MathematicallySafe.HeadCounter.Services.Forums
{
    public class ForumRaiderPosting
    {
        private static readonly IOptions Options = ObjectFactory.GetInstance<IOptions>();

        private readonly IForumFormatter format;

        private int _commaCount;
        private string _post;

        public ForumRaiderPosting(IForumFormatter format)
        {
            this.format = format;
        }

        public string Generate(Raider raider)
        {
            StartNewPost(raider);

            PostAttendance(raider);
            PostLootMetrics(raider);

            _post += Environment.NewLine;

            return _post;
        }

        public string GenerateAll(Raider raider)
        {
            StartNewPost(raider);

            PostAttendance(raider);

            PostLootMetrics(raider);
            _post += Environment.NewLine;

            PostGear(raider);
            _post += Environment.NewLine;

            PostHistory(raider);
           _post += Environment.NewLine;

            return _post;
            
        }

        public string GenerateGear(Raider raider)
        {
            StartNewPost(raider);
            PostGear(raider);

            return _post;
        }

        public string GenerateHistory(Raider raider)
        {
            StartNewPost(raider);
            PostHistory(raider);
            return _post;
        }

        private void StartNewPost(Raider raider)
        {
            _commaCount = 0;
            _post = format.Colour(format.Bold(raider.WebSafeName), raider.Class.ClassContrastColourString()) + " : ";
        }

        #region Attendance

        private void PostAttendance(Raider raider)
        {
            if (Options.ShowShortPeriodAttendance)
            {
                _post += Separator() + ShortPeriodAttendancePostlet(raider);
            }

            if (Options.ShowLongPeriodAttendance)
            {
                _post += Separator() + LongPeriodAttendancePostlet(raider);
            }

            if (Options.ShowLifetimeAttendance)
            {
                _post += Separator() + LifetimeAttendancePostlet(raider);
            }

            if (Options.ShowShortPeriodAllAttendance)
            {
                _post += Separator() + ShortPeriodAllAttendancePostlet(raider);
            }

            if (Options.ShowLongPeriodAllAttendance)
            {
                _post += Separator() + LongPeriodAllAttendancePostlet(raider);
            }

            if (Options.ShowLifetimeAllAttendance)
            {
                _post += Separator() + LifetimeAllAttendancePostlet(raider);
            }

            _post += Environment.NewLine;
            _commaCount = 0;
        }

        #endregion

        #region Loot Metrics

        private void PostLootMetrics(Raider raider)
        {
            if (Options.ShowShortPeriodLootCount)
            {
                _post += Separator() + format.Italic(" 6wk Items = " + string.Format("{0}", raider.ShortPeriodLootCount));
            }

            if (Options.ShowShortPeriodLootRate)
            {
                _post += Separator() + format.Italic(" 6wk Rate = " + string.Format("{0:F2}", raider.ShortPeriodLootRate));
            }

            if (Options.ShowShortPeriodWeightedLootRate)
            {
                _post += Separator() + format.Italic(" 6wk Weighted Rate = " + string.Format("{0:F2}", raider.ShortPeriodWeightedLootRate));
            }

            if (Options.ShowLongPeriodLootCount)
            {
                _post += Separator() + format.Italic(" 12wk Items = " + string.Format("{0}", raider.LongPeriodLootCount));
            }

            if (Options.ShowLongPeriodLootRate)
            {
                _post += Separator() + format.Italic(" 12wk Rate = " + string.Format("{0:F2}", raider.LongPeriodLootRate));
            }

            if (Options.ShowLongPeriodWeightedLootRate)
            {
                _post += Separator() + format.Italic(" 12wk Weighted Rate = " + string.Format("{0:F2}", raider.LongPeriodWeightedLootRate));
            }

            if (Options.ShowLifetimeLootCount)
            {
                _post += Separator() + format.Italic(" Items = " + string.Format("{0}", raider.LifetimeLootCount));
            }

            if (Options.ShowLifetimeLootRate)
            {
                _post += Separator() + format.Italic(" Rate = " + string.Format("{0:F2}", raider.LifetimeLootRate));
            }

            if (Options.ShowLifetimeWeightedLootRate)
            {
                _post += Separator() + format.Italic(" Weighted Rate = " + string.Format("{0:F2}", raider.LifetimeWeightedLootRate));
            }

            _post += Environment.NewLine;
            _commaCount = 0;
        }

        private void PostLootPriority(Raider raider)
        {
            if (Options.ShowShortPeriodLootPriority)
            {
                _post += Separator() + format.Bold(" 6wk Priority = " + string.Format("{0}", raider.ShortPeriodLootPriority));
            }

            if (Options.ShowLongPeriodLootPriority)
            {
                _post += Separator() + format.Bold(" 12wk Priority = " + string.Format("{0}", raider.LongPeriodLootPriority));
            }

            if (Options.ShowShortPeriodLootPriority)
            {
                _post += Separator() + format.Bold(" Priority = " + string.Format("{0}", raider.LifetimeLootPriority));
            }

            _post += Environment.NewLine;
            _commaCount = 0;
        }

        #endregion

        #region Gear

        private void PostGear(Raider raider)
        {
            var raidHistory = ObjectFactory.GetInstance<IRaidHistory>();
            raidHistory.Raids.ForEach(r => AppendGear(r, raider));
        }

        private void AppendGear(Raid raid, Raider raider)
        {
            var loot = from l in raid.Loot
                       where l.WinnerIdentity == raider.Identity
                       select l;

            if (loot.Count() > 0)
            {
                _post += raid.Date.ToShortDateString() + ": ";

                loot.ForEach(l => { _post += format.ItemLink(l.ItemName, l.Item.LootUri.ToString()) + ", "; });

                TrimComma();
                AppendNewline();
            }
        }

        #endregion

        #region History

        private void PostHistory(Raider raider)
        {
            var raidHistory = ObjectFactory.GetInstance<IRaidHistory>();

            IEnumerable<Raid> raiderHistory = from r in raidHistory.Raids
                                              where r.Date >= raider.Joined
                                              orderby r.Date descending
                                              select r;

            var basicMode = Options.UseBasicMode;

            raiderHistory.ForEach(r =>
            {
                var historyLine = new 
                {
                    r.Date,
                    r.IsOptional,
                    Attended = r.RaiderAttendedAnyChar(raider.MainName) || r.IsExtraAttendee(raider.Identity),
                    Location = r.Zone,
                    LootWon = from l in r.Loot
                              where l.Winner == raider.Identity
                              select l.Item
                };

                var color = historyLine.Attended ? "Green" : "Red";
                var optional = historyLine.IsOptional ? " (Optional)" : "";

                if (Options.UseBasicMode)
                {
                    if (historyLine.Attended)
                    {
                        _post += format.Bold(historyLine.Date.ToShortDateString() + " " + historyLine.Location + optional) + 
                            LootLine(historyLine.LootWon) + Environment.NewLine;
                    }
                    else
                    {
                        _post += format.Italic(historyLine.Date.ToShortDateString() + " " + historyLine.Location + " MISSED! " + optional) +
                            LootLine(historyLine.LootWon) + Environment.NewLine;
                    }
                }
                else
                {
                    _post += format.Colour(historyLine.Date.ToShortDateString() + " " + historyLine.Location + optional, color) + 
                        LootLine(historyLine.LootWon) + Environment.NewLine;
                }
            });
        }

        private string LootLine(IEnumerable<LootItem> lootItems)
        {
            if (lootItems.Count() > 0)
            {
                var line = " ";
                lootItems.ForEach(l => { line += format.ItemLink(l.Name, l.LootUri.ToString()) + ", "; });
                line = line.Substring(0, line.Length - 2);
                return line;
            }

            return "";
        }

        #endregion

        #region Helpers

        private void AppendNewline()
        {
            _post += Environment.NewLine;
        }

        private void TrimComma()
        {
            _post = _post.Substring(0, _post.Length - 2);
        }

        private string Separator()
        {
            var separator = "";
            if (_commaCount >= 6)
            {
                _commaCount = 0;
                separator = Environment.NewLine;
            }
            else
            {
                if (_commaCount > 0)
                {
                    separator = ", ";
                }

                _commaCount++;
            }

            return separator;
        }

        private string ShortPeriodAttendancePostlet(Raider raider)
        {
            var colour = GetAttendanceColour(raider.ShortPeriodAttendance);
            var percent = string.Format("{0:0%}", raider.ShortPeriodAttendance);

            return format.Italic("6wk% = " + format.Colour(percent, colour));
        }

        private string LongPeriodAttendancePostlet(Raider raider)
        {
            var colour = GetAttendanceColour(raider.LongPeriodAttendance);
            var percent = string.Format("{0:0%}", raider.LongPeriodAttendance);

            return format.Italic("12wk% = " + format.Colour(percent, colour));
        }

        private string LifetimeAttendancePostlet(Raider raider)
        {
            var colour = GetAttendanceColour(raider.LifetimeAttendance);
            var percent = string.Format("{0:0%}", raider.LifetimeAttendance);

            return format.Italic("Att% = " + format.Colour(percent, colour));
        }

        private string ShortPeriodAllAttendancePostlet(Raider raider)
        {
            var colour = GetAttendanceColour(raider.ShortPeriodAllAttendance);
            var percent = string.Format("{0:0%}", raider.ShortPeriodAllAttendance);

            return format.Italic("6wk% (inc. Optional) = " + format.Colour(percent, colour));
        }

        private string LongPeriodAllAttendancePostlet(Raider raider)
        {
            var colour = GetAttendanceColour(raider.LongPeriodAllAttendance);
            var percent = string.Format("{0:0%}", raider.LongPeriodAllAttendance);

            return format.Italic("12wk% (inc. Optional) = " + format.Colour(percent, colour));
        }

        private string LifetimeAllAttendancePostlet(Raider raider)
        {
            var colour = GetAttendanceColour(raider.LifetimeAllAttendance);
            var percent = string.Format("{0:0%}", raider.LifetimeAllAttendance);

            return format.Italic("Att% (inc. Optional) = " + format.Colour(percent, colour));
        }

        private string GetAttendanceColour(double attendance)
        {
            if (attendance >= Options.HighAttendancePercentage / 100.0)
            {
                return "Black";
            }

            if (attendance >= Options.GoodAttendancePercentage / 100.0)
            {
                return "#0070dd";
            }

            if (attendance >= Options.FairAttendancePercentage / 100.0)
            {
                return "Green";
            }

            return "Red";
        }

        #endregion
    }
}
